package project.parser.ast.exps.op.unary;

import project.environments.DynamicEnv;
import project.environments.StaticEnv;
import project.environments.Value;
import project.parser.TypecheckException;
import project.parser.ast.exps.Exp;
import project.parser.ast.exps.FieldAccess;
import project.parser.ast.exps.Ident;
import project.parser.ast.types.PrimType;
import project.parser.ast.types.Type;
import project.parser.ast.values.IntValue;

public abstract class IncDecr extends UnaryOp {
	protected final boolean pre; //booleano per il pre-incremento o post-incremento

	public IncDecr(Exp id) {
		this(id, false);
	}

	public IncDecr(Exp id, boolean pre) {
		super(id);
		this.pre = pre;
	}

	@Override
	public Type typecheck(StaticEnv senv) {
		if (!(exp instanceof Ident) && !(exp instanceof FieldAccess))
			throw new TypecheckException("Expected an identifier in " + opToString() + " expression");
		Type type = exp.typecheck(senv);
		if (type != PrimType.NUMBER_TYPE)
			throw new TypecheckException("Expected a number value identifier");
		return type;
	}

	@Override
	public Value evaluate(DynamicEnv denv) {
		Value value = exp.evaluate(denv);
		int newIntValue = op(value.asInteger());
		IntValue newValue = new IntValue(newIntValue);
		if (exp instanceof FieldAccess) {
			((FieldAccess)exp).set(denv, newValue); //set del campo per i record
		} else {
			denv.update((Ident)exp, newValue); //update dell'ambiente dinamico per le variabili
		}
		if (!pre)
			newIntValue = value.asInteger(); //se non e' un pre-incremento ritorno il vecchio valore non incrementato
		return new IntValue(newIntValue);
	}

	@Override
	protected String opToString() {
		StringBuilder expStr = new StringBuilder(exp.toString());
		if (pre)
			expStr.insert(0, opStr());
		else
			expStr.append(opStr());
		return expStr.toString();
	}

	abstract protected String opStr();

	abstract protected int op(int value);
}
